﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Xml.Linq;
using TheOutfield.UmbExt.UniversalMediaPicker.Extensions;

namespace TheOutfield.UmbExt.UniversalMediaPicker.Providers.SlideShare
{
    public class SlideShareService
    {
        private SlideShareConfig _config;

        public SlideShareService(string config)
        {
            _config = config.DeserializeJsonTo<SlideShareConfig>();
        }

        public SlideShareConfig Config
        {
            get { return _config; }
        }

        public IList<Slideshow> GetSlideshows()
        {
            var parameters = GetParameterBase();
            parameters.Add("username_for", _config.Username.UrlEncode());
            parameters.Add("offset", 0);
            parameters.Add("limit", 100);

            var response = GetCommand.Execute("http://www.slideshare.net/api/2/get_slideshows_by_user", parameters);
            var xml = XDocument.Parse(response);

            return (from slideshow in xml.Descendants("Slideshow") select slideshow.ToSlideshow()).ToList();
        }

        public Slideshow GetSlideshow(int id)
        {
            var parameters = this.GetParameterBase();
            parameters.Add("slideshow_id", id);

            var response = GetCommand.Execute("http://www.slideshare.net/api/2/get_slideshow", parameters);
            var xml = XDocument.Parse(response);

            return xml.Root.ToSlideshow();
        }

        public Slideshow CreateSlideshow(string title,
            string description,
            HttpPostedFile file)
        {
            var fileName = file.FileName;
            var fileContenType = file.ContentType;
            var fileContent = new byte[file.ContentLength];
            file.InputStream.Read(fileContent, 0, fileContent.Length);
            file.InputStream.Close();

            var parameters = GetParameterBase();
            parameters.Add("username", _config.Username);
            parameters.Add("password", _config.Password);
            parameters.Add("slideshow_title", title);
            parameters.Add("slideshow_description", description);

            parameters.Add("slideshow_srcfile", new PostCommand.FileParameter(fileContent, fileName, fileContenType));

            var response = PostCommand.Execute("http://www.slideshare.net/api/2/upload_slideshow", parameters);
            var xml = XDocument.Parse(response);
            if (xml.Root != null && xml.Root.Element("SlideShowID") != null)
            {
                var slideshowId = Convert.ToInt32(xml.Root.Element("SlideShowID").Value);
                if (slideshowId > 0)
                {
                    return GetSlideshow(slideshowId);
                }
            }

            return null;
        }

        private Dictionary<string, object> GetParameterBase()
        {
            var timestamp = GenerateTimestamp();
            var parameterBase = new Dictionary<string, object>();
            parameterBase.Add("api_key", _config.ApiKey);
            parameterBase.Add("ts", timestamp);
            parameterBase.Add("hash", (_config.SharedSecret + timestamp).CalculateSHA1(Encoding.UTF8));

            return parameterBase;
        }

        private string GenerateTimestamp()
        {
            return Convert.ToInt32(Math.Floor((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds)).ToString();
        }
    }

    #region Request Helpers

    public static class PostCommand
    {

        private static readonly Encoding encoding = Encoding.UTF8;

        public static string Execute(string postUrl, Dictionary<string, object> postParameters)
        {
            string formDataBoundary = "-----------------------------28947758029299";
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = GetMultipartFormData(postParameters, formDataBoundary);
            string responseXml;
            using (var response = PostForm(postUrl, contentType, formData))
            {
                var reader = new StreamReader(response.GetResponseStream());

                responseXml = reader.ReadToEnd();
            }

            return responseXml;
        }

        private static HttpWebResponse PostForm(string postUrl, string contentType, byte[] formData)
        {
            var request = WebRequest.Create(postUrl) as HttpWebRequest;

            if (request == null)
            {
                throw new NullReferenceException("request is not a http request");
            }

            // Set up the request properties
            request.Method = "POST";
            request.ContentType = contentType;
            request.CookieContainer = new CookieContainer();
            request.ContentLength = formData.Length;  // We need to count how many bytes we're sending. 

            using (Stream requestStream = request.GetRequestStream())
            {
                // Push it out there
                requestStream.Write(formData, 0, formData.Length);
                requestStream.Close();
            }

            return request.GetResponse() as HttpWebResponse;
        }

        private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
        {
            Stream formDataStream = new MemoryStream();

            foreach (var param in postParameters)
            {
                if (param.Value is FileParameter)
                {
                    var fileToUpload = (FileParameter)param.Value;

                    // Add just the first part of this param, since we will write the file data directly to the Stream
                    string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
                        boundary,
                        param.Key,
                        fileToUpload.FileName ?? param.Key,
                        fileToUpload.ContentType ?? "application/octet-stream");

                    formDataStream.Write(encoding.GetBytes(header), 0, header.Length);

                    // Write the file data directly to the Stream, rather than serializing it to a string.
                    formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
                }
                else
                {
                    string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
                        boundary,
                        param.Key,
                        param.Value);
                    formDataStream.Write(encoding.GetBytes(postData), 0, postData.Length);
                }
            }

            // Add the end of the request
            string footer = "\r\n--" + boundary + "--\r\n";
            formDataStream.Write(encoding.GetBytes(footer), 0, footer.Length);

            // Dump the Stream into a byte[]
            formDataStream.Position = 0;
            byte[] formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();

            return formData;
        }

        public class FileParameter
        {
            public byte[] File { get; set; }
            public string FileName { get; set; }
            public string ContentType { get; set; }
            public FileParameter(byte[] file) : this(file, null) { }
            public FileParameter(byte[] file, string filename) : this(file, filename, null) { }
            public FileParameter(byte[] file, string filename, string contenttype)
            {
                File = file;
                FileName = filename;
                ContentType = contenttype;
            }
        }
    }

    public static class GetCommand
    {
        public static string Execute(string url, ICollection<KeyValuePair<string, object>> parameters)
        {
            var request = WebRequest.Create(url + "?" + CreateFormattedRequest(parameters)) as HttpWebRequest;
            string responseXml;

            using (var response = request.GetResponse() as HttpWebResponse)
            {
                var reader = new StreamReader(response.GetResponseStream());
                responseXml = reader.ReadToEnd();
            }

            return responseXml;
        }

        private static string CreateFormattedRequest(ICollection<KeyValuePair<string, object>> values)
        {
            var parameterBuilder = new StringBuilder();
            var counter = 0;
            foreach (var value in values)
            {
                parameterBuilder.AppendFormat("{0}={1}", value.Key, value.Value);

                if (counter != values.Count - 1)
                {
                    parameterBuilder.Append("&");
                }

                counter++;
            }

            return parameterBuilder.ToString();
        }
    }

    #endregion

    #region Model

    public class Slideshow
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public SlideshowStatus Status { get; set; }
        public string Username { get; set; }
        public string Url { get; set; }
        public string ThumbnailUrl { get; set; }
        public string ThumbnailSmallUrl { get; set; }
        public string EmbedCode { get; set; }
        public DateTime Created { get; set; }
        public DateTime Updated { get; set; }
        public string Language { get; set; }
        public string Format { get; set; }
        public bool Download { get; set; }
        public string DownloadUrl { get; set; }
        public SlideshowType Type { get; set; }
    }

    public enum SlideshowStatus
    {
        Queued,
        Converting,
        Converted,
        Failed
    }

    public enum SlideshowType
    {
        Presentation,
        Document,
        Portfolio,
        Video
    }

    #endregion
}